﻿/*========================== 
 * @author 郑金泉
 * @desc JavaScript库信息类
 * ========================= */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

using Obsidian.Utils;
using System.Xml;

namespace Obsidian.Web
{
    public class JsLibrary
    {

        private static string CONFIG_PATH = RuntimeUtil.BinPath + "\\JsLibs.xml";
        private const string CSS_INCLUDE_HTML = "<link href=\"{0}\" type=\"text/css\" rel=\"stylesheet\">";
        private const string JS_INCLUDE_HTML = "<script src=\"{0}\" type=\"text/javascript\" langugae=\"javascript\"></script>";
        private const string CSS_IMPORT_HTML = "<style type=\"text/css\">{0}</style>";
        private const string JS_IMPORT_HTML = "<script type=\"text/javascript\">{0}</script>";
        private static Dictionary<string, LibInfo> dictLibs = null;
        private static DateTime configModified = DateTime.MinValue;

        public static string IncludeHtml(string libs)
        {
            StringBuilder sb = new StringBuilder();
            string[] names = libs.Split(',');
            foreach (string name in names)
            {
                LibInfo lib = GetLibInfo(name.Trim());
                if (lib == null)
                    throw new Exception("JsLibs： " + name + " 配置项不存在");
                foreach (LibFile libFile in lib.Files)
                {
                    if (libFile.Mode.Equals("link"))
                    {
                        string html = libFile.Type.Equals("js") ? JS_INCLUDE_HTML : CSS_INCLUDE_HTML;
                        sb.AppendLine(String.Format(html, libFile.Path + "?t=" + libFile.Modified.ToString("mmss")));
                    }
                    else if (libFile.Mode.Equals("import"))
                    {
                        string html = libFile.Type.Equals("js") ? JS_IMPORT_HTML : CSS_IMPORT_HTML;
                        string codes = IOUtil.ReadTextFromFile(WebUtil.MapPath(libFile.Path), "utf-8");
                        sb.AppendLine(String.Format(html, codes));
                    }
                }
            }
            return sb.ToString();
        }

        private static LibInfo GetLibInfo(string name)
        {
            CheckChange();
            LibInfo lib;
            if (dictLibs.TryGetValue(name, out lib))
                return lib;
            else
                return null;
        }

        private static bool CheckChange()
        {
            bool isChanged = false;
            FileInfo file = new FileInfo(CONFIG_PATH);
            if (dictLibs == null || !configModified.Equals(file.LastWriteTime))
            {
                isChanged = true;
                if (dictLibs != null)
                    dictLibs.Clear();
                XmlDocument xmlDoc = new XmlDocument();
                xmlDoc.Load(CONFIG_PATH);

                XmlNodeList nodeList = xmlDoc.SelectNodes("libs/lib");
                dictLibs = new Dictionary<string, LibInfo>(nodeList.Count);
                lock(dictLibs)
                {
                    XmlAttribute attr;
                    foreach (XmlNode node in nodeList)
                    {
                        LibInfo lib = new LibInfo();
                        string name;
                        if(!XmlUtil.TryGetAttrValue(node, "name", out name))
                            throw new Exception("配置项 lib 的 name 属性不能为空");
                        if (dictLibs.ContainsKey(name))
                            throw new Exception("存在相同配置项名称：" + name);

                        List<LibFile> libFiles = new List<LibFile>();
                        XmlNodeList fileNodes = node.SelectNodes("file");
                        foreach (XmlNode fileNode in fileNodes)
                        {
                            string path;
                            if(!XmlUtil.TryGetAttrValue(fileNode, "path", out path))
                                throw new Exception("配置项 file 的 path 属性不能为空");

                            string type;
                            if (!XmlUtil.TryGetAttrValue(fileNode, "type", out type))
                                throw new Exception("配置项 file 的 type 属性不能为空");
                            if (!type.Equals("js") && !type.Equals("css"))
                                throw new Exception("配置项 file 的 type 属性值必须是 js 或 css ");

                            string mode;
                            if (XmlUtil.TryGetAttrValue(fileNode, "mode", out mode))
                            {
                                if (!mode.Equals("link") && !mode.Equals("import"))
                                    throw new Exception("配置项 file 的 mode 属性值必须是 link 或 import ");
                            }
                            else 
                            {
                                mode = "link";
                            }

                            string fullPath = WebUtil.MapPath(path);
                            if (!File.Exists(fullPath))
                                throw new Exception("路径：" + path + " 不存在");

                            LibFile libFile = new LibFile();
                            libFile.Path = path;
                            libFile.Type = type;
                            libFile.Mode = mode;
                            libFile.Modified = new FileInfo(fullPath).LastWriteTime;
                            libFiles.Add(libFile);

                        }
                        lib.Files = libFiles;
                        dictLibs.Add(name, lib);
                    }
                }
                configModified = file.LastWriteTime;
            }
            return isChanged;
        }

        private class LibInfo
        {
            private List<LibFile> files = new List<LibFile>();

            public string Name { get; set; }

            public List<LibFile> Files
            {
                set { this.files = value; }
                get { return this.files; }
            }
        }

        private class LibFile
        {
            public string Path { get; set; }
            public string Type { get; set; }
            public string Mode { get; set; }
            public DateTime Modified { get; set; }
        }

    }
}
